﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Kinect;
using BicycleTrafficSimulator.DataModel;
using System.Threading;
using System.IO;
using System.Reflection;

namespace BicycleTrafficSimulator
{
    public class City : AbstractModel
    {     
        private Texture2D sceneryTexture;
        private VertexBuffer cityVertexBuffer;
        private Effect effect;
        private float imagesInTexture;
        private int nrDifferentBuildings;
        private float sidewalkHeight;

        public float SidewalkHeight
        {
            get { return sidewalkHeight; }
            set { sidewalkHeight = value; }
        }

        private int[][] cityPlan;
        public int[][] CityPlan
        {
            get { return cityPlan; }
            set { cityPlan = value; }
        }
        private int[] buildingHeights;

        public int[] BuildingHeights
        {
            get { return buildingHeights; }
            set { buildingHeights = value; }
        }
        private int cityWidth, cityLength;

        public int CityLength
        {
            get { return cityLength; }
            set { cityLength = value; }
        }

        public int CityWidth
        {
            get { return cityWidth; }
            set { cityWidth = value; }
        }

        public City(String cityMapFile)
        {
            this.buildingHeights = new int[] { 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
            string appDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            this.cityPlan = File.ReadLines(Path.Combine(Path.GetDirectoryName(appDirectory), @"..\..\..\") + "BicycleTrafficSimulatorContent" + "/" + cityMapFile).Select(l => l.Split(',').Select(int.Parse).ToArray()).ToArray();   
            this.cityWidth = cityPlan.Count();
            this.cityLength = cityPlan.First().Count();
            this.sidewalkHeight = 0.03f;
            this.imagesInTexture = 16;
            this.nrDifferentBuildings = buildingHeights.Length - 10;
        }

        public void Update(GameTime gameTime) { }

        public void LoadContent(ContentManager content, String cityTexture, GraphicsDevice device)
        {
            this.effect = content.Load<Effect>("effects");
            this.sceneryTexture = content.Load<Texture2D>(cityTexture);
            SetUpVertices(device);
        }

        public void UnloadContent()
        {
            effect.Dispose();
            sceneryTexture.Dispose();
        }


        private void SetUpVertices(GraphicsDevice device)
        {

            int textureType;
            List<VertexPositionNormalTexture> verticesList = new List<VertexPositionNormalTexture>();
            Random random = new Random();

            for (int x = 0; x < cityWidth; x++)
            {
                for (int z = 0; z < cityLength; z++)
                {
                    if (cityPlan[x][z] > 19)
                        textureType = 9;
                    else if (cityPlan[x][z] == 10)
                        textureType = random.Next(nrDifferentBuildings) + 10;
                    else textureType = cityPlan[x][z];
                    

                    // street
                    if ((textureType == 0) || (textureType == 1) || (textureType == 2) || (textureType == 3) || (textureType == 4) || (textureType == 5) || (textureType == 6) || (textureType == 7) || (textureType == 8))
                    {                       
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[1], -z), new Vector3(0, 1, 0), new Vector2(textureType / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[1], -z - 1), new Vector3(0, 1, 0), new Vector2((textureType / imagesInTexture), 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[1], -z), new Vector3(0, 1, 0), new Vector2((textureType + 1) / imagesInTexture, 1)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[1], -z - 1), new Vector3(0, 1, 0), new Vector2((textureType / imagesInTexture), 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[1], -z - 1), new Vector3(0, 1, 0), new Vector2((textureType + 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[1], -z), new Vector3(0, 1, 0), new Vector2((textureType + 1) / imagesInTexture, 1)));

                    }
                    // sidewalk
                    else if (textureType == 9)
                    {
                        //floor or ceiling
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z), new Vector3(0, 1, 0), new Vector2(textureType / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z - 1), new Vector3(0, 1, 0), new Vector2((textureType / imagesInTexture), 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, sidewalkHeight, -z), new Vector3(0, 1, 0), new Vector2((textureType + 1) / imagesInTexture, 1)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z - 1), new Vector3(0, 1, 0), new Vector2((textureType / imagesInTexture), 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, sidewalkHeight, -z - 1), new Vector3(0, 1, 0), new Vector2((textureType + 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, sidewalkHeight, -z), new Vector3(0, 1, 0), new Vector2((textureType + 1) / imagesInTexture, 1)));

                         //front wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((textureType + 1)  / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z - 1), new Vector3(0, 0, -1), new Vector2(textureType / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z - 1), new Vector3(0, 0, -1), new Vector2(textureType / imagesInTexture, 1)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z - 1), new Vector3(0, 0, -1), new Vector2(textureType / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((textureType + 1) / imagesInTexture, 1)));
                       verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, sidewalkHeight, -z - 1), new Vector3(0, 0, -1), new Vector2((textureType + 1) / imagesInTexture, 0)));

                        //back wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(0, 0, 1), new Vector2((textureType  + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(0, 0, 1), new Vector2(textureType  / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z), new Vector3(0, 0, 1), new Vector2(textureType / imagesInTexture, 0)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z), new Vector3(0, 0, 1), new Vector2(textureType / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, sidewalkHeight, -z), new Vector3(0, 0, 1), new Vector2((textureType + 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(0, 0, 1), new Vector2((textureType  + 1) / imagesInTexture, 1)));

                        //left wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(-1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z - 1), new Vector3(-1, 0, 0), new Vector2(textureType  / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z - 1), new Vector3(-1, 0, 0), new Vector2(textureType / imagesInTexture, 0)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z - 1), new Vector3(-1, 0, 0), new Vector2(textureType / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, sidewalkHeight, -z), new Vector3(-1, 0, 0), new Vector2((textureType + 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(-1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 1)));

                        //right wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, sidewalkHeight, -z - 1), new Vector3(1, 0, 0), new Vector2(textureType / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(1, 0, 0), new Vector2(textureType / imagesInTexture, 1)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, sidewalkHeight, -z - 1), new Vector3(1, 0, 0), new Vector2(textureType / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, sidewalkHeight, -z), new Vector3(1, 0, 0), new Vector2((textureType + 1) / imagesInTexture, 0)));                 
                   }       
                    // buildings
                    else
                    {
                        //front wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((textureType + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[textureType], -z - 1), new Vector3(0, 0, -1), new Vector2(textureType / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z - 1), new Vector3(0, 0, -1), new Vector2(textureType / imagesInTexture, 1)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[textureType], -z - 1), new Vector3(0, 0, -1), new Vector2(textureType / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((textureType + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[textureType], -z - 1), new Vector3(0, 0, -1), new Vector2((textureType + 1) / imagesInTexture, 0)));

                        //back wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(0, 0, 1), new Vector2((textureType  + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(0, 0, 1), new Vector2(textureType  / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[textureType], -z), new Vector3(0, 0, 1), new Vector2(textureType  / imagesInTexture, 0)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[textureType], -z), new Vector3(0, 0, 1), new Vector2(textureType  / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[textureType], -z), new Vector3(0, 0, 1), new Vector2((textureType  + 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(0, 0, 1), new Vector2((textureType  + 1) / imagesInTexture, 1)));

                        //left wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(-1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z - 1), new Vector3(-1, 0, 0), new Vector2(textureType  / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[textureType], -z - 1), new Vector3(-1, 0, 0), new Vector2(textureType  / imagesInTexture, 0)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[textureType], -z - 1), new Vector3(-1, 0, 0), new Vector2(textureType  / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[textureType], -z), new Vector3(-1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(-1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 1)));

                        //right wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[textureType], -z - 1), new Vector3(1, 0, 0), new Vector2(textureType  / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(1, 0, 0), new Vector2(textureType / imagesInTexture, 1)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[textureType], -z - 1), new Vector3(1, 0, 0), new Vector2(textureType  / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[textureType], -z), new Vector3(1, 0, 0), new Vector2((textureType  + 1) / imagesInTexture, 0)));
                    }
                }
            }

            cityVertexBuffer = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration, verticesList.Count, BufferUsage.WriteOnly);

            cityVertexBuffer.SetData<VertexPositionNormalTexture>(verticesList.ToArray());
        }

        public void Draw(Matrix viewMatrix, Matrix projectionMatrix, Vector3 lightDirection, GraphicsDevice device, Vector3 campos, Vector3 camup)
        {

            effect.CurrentTechnique = effect.Techniques["Textured"];
            effect.Parameters["xWorld"].SetValue(Matrix.Identity);
            effect.Parameters["xView"].SetValue(viewMatrix);
            effect.Parameters["xProjection"].SetValue(projectionMatrix);
            effect.Parameters["xTexture"].SetValue(sceneryTexture);

            effect.Parameters["xEnableLighting"].SetValue(true);
            effect.Parameters["xLightDirection"].SetValue(lightDirection);
            effect.Parameters["xAmbient"].SetValue(0.5f);
            effect.Parameters["xCamPos"].SetValue(campos);
            effect.Parameters["xCamUp"].SetValue(camup);

            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();
                device.SetVertexBuffer(cityVertexBuffer);
                device.DrawPrimitives(PrimitiveType.TriangleList, 0, cityVertexBuffer.VertexCount / 3);
                effect.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
                effect.GraphicsDevice.BlendState = BlendState.Opaque;

            }
        }

         
     }
    }

